package com.pingan.haofang.searchcloud.common.dto;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import org.springframework.data.domain.Page;

import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 分页DTO
 *
 * @author BAOJIE371
 * @date 2017/2/20
 * @since 1.0.0
 */
@ApiModel
public final class PageDTO<V> {

    @ApiModelProperty("总记录数")
    private final long total;

    @ApiModelProperty("总页数")
    private final int totalPages;

    @ApiModelProperty("数据集")
    private final List<V> datas;

    @ApiModelProperty("当前页码")
    private final int pageNo;

    @ApiModelProperty("每页显示记录数")
    private final int pageSize;

    private PageDTO(long total, int totalPages, int pageNo, int pageSize, List<V> datas) {
        this.total = total;
        this.totalPages = totalPages;
        this.pageNo = pageNo;
        this.pageSize = pageSize;
        this.datas = datas;
    }

    /**
     * Builder构造器
     *
     * @param datas
     * @param <V>
     * @return
     */
    public static <V> Builder<V> builder(List<V> datas) {
        return new Builder<>(datas);
    }

    /**
     * 将Page转换为PageDTO
     *
     * @param page
     * @param <V>
     * @return
     */
    public static <V> PageDTO<V> of(Page<V> page) {
        Objects.requireNonNull(page, "page is null");
        return new PageDTO<>(page.getTotalElements(), page.getTotalPages(),
                page.getNumber() + 1, page.getSize(),
                Optional.ofNullable(page.getContent())
                        .orElse(Collections.emptyList()));
    }

    /**
     * 将Page转换为PageDTO，需提供转换器对列表数据进行转换
     *
     * @param page
     * @param converter
     * @param <T>
     * @param <V>
     * @return
     */
    public static <T, V> PageDTO<V> of(Page<T> page, Function<T, V> converter) {

        Objects.requireNonNull(page, "page is null");
        Objects.requireNonNull(converter, "converter is null");

        return new PageDTO<>(page.getTotalElements(), page.getTotalPages(),
                page.getNumber() + 1, page.getSize(),
                Optional.ofNullable(page.getContent())
                        .map(content -> content.stream()
                                .map(converter)
                                .collect(Collectors.toList()))
                        .orElse(Collections.emptyList()));
    }

    /**
     * 根据转换器构造PageDTO
     *
     * @param page
     * @param converter
     * @param <T>
     * @param <V>
     * @return
     */
    public static <T, V> PageDTO<V> of(PageDTO<T> page, Function<T, V> converter) {
        Objects.requireNonNull(page, "page is null");
        Objects.requireNonNull(converter, "converter is null");
        return new PageDTO<>(page.getTotal(), page.getTotalPages(),
                page.getPageNo(), page.getPageSize(),
                Optional.ofNullable(page.getDatas())
                        .map(content -> content.stream()
                                .map(converter)
                                .collect(Collectors.toList()))
                        .orElse(Collections.emptyList()));
    }

    public final long getTotal() {
        return total;
    }

    public final int getTotalPages() {
        return totalPages;
    }

    public final List<V> getDatas() {
        return datas;
    }

    public final int getPageNo() {
        return pageNo;
    }

    public final int getPageSize() {
        return pageSize;
    }

    /**
     * Builder构造器
     *
     * @param <V>
     */
    public static class Builder<V> {
        private long totalRecords;
        private int totalPages = 1;
        private final List<V> datas;
        private int pageNo = 1;
        private int pageSize;

        private Builder(List<V> datas) {
            Objects.requireNonNull(datas, "datas is null");
            this.datas = datas;
            totalRecords = datas.size();
            pageSize = datas.size();
        }

        public final Builder<V> totalRecords(long totalRecords) {
            this.totalRecords = totalRecords;
            return this;
        }

        public final Builder<V> totalPages(int totalPages) {
            this.totalPages = totalPages;
            return this;
        }

        public final Builder<V> pageNo(int pageNo) {
            this.pageNo = pageNo;
            return this;
        }

        public final Builder<V> pageSize(int pageSize) {
            this.pageSize = pageSize;
            return this;
        }

        public final PageDTO<V> build() {
            return new PageDTO<V>(totalRecords, totalPages, pageNo, pageSize, datas);
        }
    }
}

